#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

#include "head.h"

#define CNAME "hello"
int major = 0;
unsigned int *pwm_gpio_base = NULL;
unsigned int *pwm_base = NULL;
char kbuf[128] = {0};
struct class *cls;
struct device *dev;
int pwm_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}
ssize_t pwm_read(struct file *file, char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	if (size > sizeof(kbuf))
		size = sizeof(kbuf);
	ret = copy_to_user(ubuf, kbuf, size);
	if (ret)
	{
		printk("copy data to user error\n");
		return -EIO;
	}

	return size;
}

ssize_t pwm_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	if (size > sizeof(kbuf))
		size = sizeof(kbuf);
	ret = copy_from_user(kbuf, ubuf, size);
	if (ret)
	{
		printk("copy data from user error\n");
		return -EIO;
	}

	return size;
}
long pwm_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	switch (cmd)
	{
	case PWM_ON:
		// 璁剧疆寮曡剼涓篜WM鍔熻兘
		*(pwm_gpio_base + 8) &= (~(0x3 << 28));
		*(pwm_gpio_base + 8) |= (0x2 << 28);
		// 璁剧疆涓€绾у垎棰戝€�  250
		*(pwm_base) &= (~(0xFF << 8));
		*(pwm_base) |= (249 << 8);
		// 璁剧疆浜岀骇1鍒嗛鍊�
		*(pwm_base + 1) &= (~(0xF << 8));
		*(pwm_base + 1) |= (0x1 << 8);
		// 璁剧疆TCNTB2鍒濆鍊�  1000Hz
		*(pwm_base + 6) = 300;
		// 璁剧疆TCMPB2鐨勫垵濮嬪€�  50%
		*(pwm_base + 7) = 150;
		// 鎵撳紑缈昏浆
		*(pwm_base + 1) |= (1 << 14);
		// 寮€鍚墜鍔ㄥ姞杞�
		*(pwm_base + 1) |= (1 << 13);
		// 寮€鍚嚜鍔ㄥ姞杞�
		*(pwm_base + 1) |= (1 << 15);
		// 鍏抽棴鎵嬪姩鍔犺浇
		*(pwm_base + 1) &= (~(1 << 13));
		// 鎵撳紑PWM瀹氭椂鍣�
		*(pwm_base + 1) |= (1 << 12);
		break;
	case PWM_OFF:
		//鍏抽棴pwm铚傞福鍣ㄨ缃甮pio鍙ｄ负闈瀙wm鐨勫叾浠栧姛鑳�
		*(pwm_gpio_base + 8) &= (~(0x3 << 28));
		break;

	case ACCESS_DATA_W:
		ret = copy_from_user(kbuf, (void *)args, sizeof(kbuf));
		if (ret)
		{
			printk("copy data from user error\n");
			return -EIO;
		}
		printk("kbuf = %s\n", kbuf);
		break;
	}

	return 0;
}

int pwm_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = pwm_open,
	.read = pwm_read,
	.write = pwm_write,
	.unlocked_ioctl = pwm_ioctl,
	.release = pwm_close,
};

//閸忋儱褰�
static int __init demo_init(void)
{
	int i;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	major = register_chrdev(0, CNAME, &fops);
	if (major <= 0)
	{
		printk("register char devices driver error\n");
		return -EAGAIN;
	}
	printk("major = %d\n", major);

	//鐏忓攱ed閻ㄥ嫬婀撮崸鈧弰鐘茬殸閸戠儤娼�
	pwm_gpio_base = ioremap(0xc001c000, 40);
	if (pwm_gpio_base == NULL)
	{
		printk("ioremap gpio pwd error\n");
		return -ENOMEM;
	}
	pwm_base = ioremap(0xc0018000, 40);
	if (pwm_gpio_base == NULL)
	{
		printk("ioremap pwd error\n");
		return -ENOMEM;
	}
	//鍒涘缓class
	cls = class_create(THIS_MODULE, "class-hello");
	if (IS_ERR(cls))
	{
		printk("class create error\n");
		return PTR_ERR(cls);
	}
	//鍒涘缓device
	for (i = 0; i < 3; i++)
	{
		dev = device_create(cls, NULL, MKDEV(major, i),
							NULL, "dev-pwm%d", i);
	}
	return 0;
}
//閸戝搫褰�
static void __exit demo_exit(void)
{
	int i;
	for (i = 0; i < 3; i++)
	{
		device_destroy(cls, MKDEV(major, i));
	}
	class_destroy(cls);
	iounmap(pwm_gpio_base);
	iounmap(pwm_base);
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	unregister_chrdev(major, CNAME);
}

module_init(demo_init);
module_exit(demo_exit);
//鐠佺ǹ褰茬拠锟�
MODULE_LICENSE("GPL");
